package org.reborn.demo

import org.reborn.engine.app._
import org.reborn.engine.render._
import org.reborn.engine.render.camera.EulerAngleCamera
import org.reborn.math.Axis
import org.reborn.engine.input.Keyboard._
import org.reborn.math._

import scala.math
import scala.List

import org.reborn.utils.niobuffer.BufferUtil
import org.reborn.engine.render.texture._
import org.reborn.engine.model.mesh.{Mesh,MutableMesh,VertexData,MeshUtil,MeshConnectivity,MeshShapes}
import org.reborn.engine.enums.ArrayType
import org.reborn.engine.enums.FaceMode
import org.reborn.engine.enums.UpdateFrequency

import org.reborn.utils.HeightMapUtil._
import org.reborn.engine.render.util._
import org.reborn.utils.Util._
import org.reborn.Constants._

import java.awt._
import java.awt.event._;

import javax.media.opengl._
import javax.media.opengl.GL
import javax.media.opengl.fixedfunc.GLLightingFunc
import javax.media.opengl.fixedfunc.GLMatrixFunc
import javax.media.opengl.glu._
import com.jogamp.opengl.util._
import com.jogamp.opengl.util.awt.TextRenderer

import GL._
import GLLightingFunc._
import GLMatrixFunc._
import GL2._
import GL2ES1._
import GL2GL3._
    
/*
 * This application creates a heightmap and allows the user to
 * fly around the scene using the mouse and keyboard
 * 
 */
object FlyThroughCamera extends GLApp {
  
  def main(args:Array[String]){
	
	//Set up some application options 
    val options = new GLAppOptions()
    options.title = "Fly Through Camera Test"
    options.fullscreen = false
    options.debugGL = true
    
    //start the application
	startApp(options)
  }

  //Set up the variables we are going to use 
  var dt = 0.0f;
  val text = new TextRenderer(new Font("SansSerif", Font.BOLD, 20))
  
  var texZoom = 1
  
  var meshId = -1;
  var tex:Texture = null
  
  var fpsAvg = 0.0f;
  
  val camera = new EulerAngleCamera(Axis.Y,Axis.X,Axis.Z)
  camera.position_= ( VectorF(0f,.5f,0f))
  
  var hideMouse = false
  var centerMouse = false
  var lastPressed = 0.0f
  val minPressedTime = 0.25f

  
  override def initScene(drawable:GLAutoDrawable){
    val gl = drawable.getGL.getGL2
    import gl._
    
    //Set some OpenGL properties
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LEQUAL)
    glHint(GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST)
    glClearColor(0.3f,0.3f,0.3f,1.0f)
    
    //Enable lighting
    glEnable(GL_LIGHTING)
    glShadeModel(GL_SMOOTH);
    glEnable(GL_COLOR_MATERIAL)
    glEnable(GL_LIGHT0)
    
    glLightfv(GL_LIGHT0,GL_POSITION,Array(0.0f,-1.0f,0.5f,0.0f),0)
    glLightfv(GL_LIGHT0,GL_AMBIENT,Array(0.3f,0.3f,0.3f,1.0f),0)
    glLightfv(GL_LIGHT0,GL_DIFFUSE,Array(1.0f,1.0f,1.0f,1.0f),0)
    glLightfv(GL_LIGHT0,GL_SPECULAR,Array(1.0f,1.0f,1.0f,1.0f),0)
    
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
    glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,Array(1.0f,1.0f,1.0f,1.0f),0)
    glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,Array(0.0f,0.0f,0.0f,0.0f),0)
    glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,40.0f)
    
    val maxHeight = 1.0f
    
    //Create a heightmap using the DiamondSquare algorithm
	val hm = DiamondSquare(8,0.5f,0.1f,0.40f)
	hm.scale(0.0f,maxHeight)
	
	//Create a mesh from the heightmap
    val mesh = MeshShapes.TriangleMesh(hm,2.0f)

    //Register the mesh to be rendered using VBOs
	meshId = util.meshRenderer.register(gl,mesh,UpdateFrequency.Rarely )
	
	//Create a checkerboard texture
	val texBuffer = BufferUtil.createDirectFloatBuffer(4*3)
    for(i <- 0 until 4){
      val col = if(i==0 || i==3) 1.0f else 0.25f
      
      texBuffer.put(col).put(col).put(col)
    }
    glEnable(GL_TEXTURE_2D);
    TextureManager.minFilter = TextureFilter.Nearest
    TextureManager.magFilter = TextureFilter.Nearest
    tex = TextureManager.Texture2D(gl,Array(texBuffer),2,2,GL_RGB,GL_FLOAT)
  }
  
  override def updateLogic(seconds:Float){
    this.dt = seconds

    //Calculate the rolling average fps over the past 25 frames
    fpsAvg = ((25.0f * fpsAvg) + seconds) / 26.0f
    
    val mouse = input.mouse
    val keyboard = input.keyboard
    
    //Map left-right mouse movement to rotation around the Y axis
    //and up-down movement to rotation around the X axis
    camera.yRotation += mouse.changeX.value * dt *.5f
    camera.xRotation += mouse.changeY.value * dt *.5f

    //Get the direction that the camera is facing so that we
    //can move it forward or backward (W and S keys)
    val forward = camera.direction.ofLength(3)
    if(keyboard.keydown(Keys.W))
      camera.position = camera.position + (forward * dt * 0.1f)
    if(keyboard.keydown(Keys.S))
      camera.position = camera.position - (forward * dt * 0.1f)
    
    //Calculate a vector that points perpendicular to the camera
    //direction so that we can strafe left and right (A and D keys)
    val strafe = forward.cross(VectorF(0f,1f,0f)).normalize
    if(keyboard.keydown(Keys.D))
      camera.position = camera.position + (strafe * dt * 0.1f)
    if(keyboard.keydown(Keys.A))
      camera.position = camera.position - (strafe * dt * 0.1f)
    
    //Toggle hiding and centering the mouse cursor
    lastPressed += seconds
    if(lastPressed >= minPressedTime){
      if(keyboard.keydown(Keys.H)){
    	hideMouse = !hideMouse
    	showCursor(!hideMouse)
    	lastPressed = 0.0f
      }
      if(keyboard.keydown(Keys.C)){
    	centerMouse = !centerMouse
    	immobilizeCursor(centerMouse)
    	lastPressed = 0.0f
      }
    }
    
    //Just a neat little bonus feature to change the amount that 
    //the texture wraps over the surface of the mesh  
    val chgScroll = math.round(mouse.changeScrollWheel.value)
    if(texZoom > 0 && chgScroll < 0)
      texZoom = texZoom >> 1
    else if(texZoom < 2048 && chgScroll > 0)   
      texZoom = if(texZoom==0) 1 else texZoom << 1
  }
  
  override def renderScene(drawable:GLAutoDrawable){
    val gl = drawable.getGL.getGL2
    import gl._
    
    //Clear the depth and color buffers so that we have a 
    //blank screen to render to
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)
    glEnable(GL_LIGHTING)
    
    //Reset the current modelview matrix
    glLoadIdentity
    
    camera.setupCamera(gl)
    
    glLightfv(GL_LIGHT0,GL_POSITION,Array(0f,.5f,1f,0.0f),0)
    
    
    //Bind the texture
    if(texZoom <= 0){
      glDisable(GL_TEXTURE_2D)
      glColor4f(1f,1f,1f,1f)
    }
    else{
      glEnable(GL_TEXTURE_2D);
      tex.bind(gl)
      glMatrixMode(GL_TEXTURE)
	  glScalef(texZoom,texZoom,texZoom)
      glMatrixMode(GL_MODELVIEW)
    }
    
    
    //Render the mesh
    util.meshRenderer.render(gl,meshId)
    
    glMatrixMode(GL_TEXTURE)
	glLoadIdentity
    glMatrixMode(GL_MODELVIEW)

    //Render the fps to the screen
    text beginRendering(drawable.getWidth, drawable.getHeight)
    text setColor(1.0f, 1.0f, 1.0f, 1.0f);
    text draw("" + 1.0f/dt,0,0);
    text draw("" + 1.0f/fpsAvg,0,16);
    val pos = camera.position
    val dir = camera.direction
    text draw("Position: <%4.2f,%4.2f,%4.2f>".format(pos(0),pos(1),pos(2)),0,36)
    text draw("Direction: <%4.2f,%4.2f,%4.2f>".format(dir(0),dir(1),dir(2)),0,56)
    text endRendering
      
    glFlush
  }
}